home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / Mail / list.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-08  |  14.4 KB  |  759 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. static char sccsid[] = "@(#)list.c    5.13 (Berkeley) 4/1/89";
  20. #endif /* not lint */
  21.  
  22. #include "rcv.h"
  23. #include <ctype.h>
  24.  
  25. /*
  26.  * Mail -- a mail program
  27.  *
  28.  * Message list handling.
  29.  */
  30.  
  31. /*
  32.  * Convert the user string of message numbers and
  33.  * store the numbers into vector.
  34.  *
  35.  * Returns the count of messages picked up or -1 on error.
  36.  */
  37.  
  38. getmsglist(buf, vector, flags)
  39.     char *buf;
  40.     int *vector;
  41. {
  42.     register int *ip;
  43.     register struct message *mp;
  44.  
  45.     if (msgCount == 0) {
  46.         *vector = 0;
  47.         return 0;
  48.     }
  49.     if (markall(buf, flags) < 0)
  50.         return(-1);
  51.     ip = vector;
  52.     for (mp = &message[0]; mp < &message[msgCount]; mp++)
  53.         if (mp->m_flag & MMARK)
  54.             *ip++ = mp - &message[0] + 1;
  55.     *ip = 0;
  56.     return(ip - vector);
  57. }
  58.  
  59. /*
  60.  * Mark all messages that the user wanted from the command
  61.  * line in the message structure.  Return 0 on success, -1
  62.  * on error.
  63.  */
  64.  
  65. /*
  66.  * Bit values for colon modifiers.
  67.  */
  68.  
  69. #define    CMNEW        01        /* New messages */
  70. #define    CMOLD        02        /* Old messages */
  71. #define    CMUNREAD    04        /* Unread messages */
  72. #define    CMDELETED    010        /* Deleted messages */
  73. #define    CMREAD        020        /* Read messages */
  74.  
  75. /*
  76.  * The following table describes the letters which can follow
  77.  * the colon and gives the corresponding modifier bit.
  78.  */
  79.  
  80. struct coltab {
  81.     char    co_char;        /* What to find past : */
  82.     int    co_bit;            /* Associated modifier bit */
  83.     int    co_mask;        /* m_status bits to mask */
  84.     int    co_equal;        /* ... must equal this */
  85. } coltab[] = {
  86.     'n',        CMNEW,        MNEW,        MNEW,
  87.     'o',        CMOLD,        MNEW,        0,
  88.     'u',        CMUNREAD,    MREAD,        0,
  89.     'd',        CMDELETED,    MDELETED,    MDELETED,
  90.     'r',        CMREAD,        MREAD,        MREAD,
  91.     0,        0,        0,        0
  92. };
  93.  
  94. static    int    lastcolmod;
  95.  
  96. markall(buf, f)
  97.     char buf[];
  98. {
  99.     register char **np;
  100.     register int i;
  101.     register struct message *mp;
  102.     char *namelist[NMLSIZE], *bufp;
  103.     int tok, beg, mc, star, other, valdot, colmod, colresult;
  104.  
  105.     valdot = dot - &message[0] + 1;
  106.     colmod = 0;
  107.     for (i = 1; i <= msgCount; i++)
  108.         unmark(i);
  109.     bufp = buf;
  110.     mc = 0;
  111.     np = &namelist[0];
  112.     scaninit();
  113.     tok = scan(&bufp);
  114.     star = 0;
  115.     other = 0;
  116.     beg = 0;
  117.     while (tok != TEOL) {
  118.         switch (tok) {
  119.         case TNUMBER:
  120. number:
  121.             if (star) {
  122.                 printf("No numbers mixed with *\n");
  123.                 return(-1);
  124.             }
  125.             mc++;
  126.             other++;
  127.             if (beg != 0) {
  128.                 if (check(lexnumber, f))
  129.                     return(-1);
  130.                 for (i = beg; i <= lexnumber; i++)
  131.                     if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
  132.                         mark(i);
  133.                 beg = 0;
  134.                 break;
  135.             }
  136.             beg = lexnumber;
  137.             if (check(beg, f))
  138.                 return(-1);
  139.             tok = scan(&bufp);
  140.             regret(tok);
  141.             if (tok != TDASH) {
  142.                 mark(beg);
  143.                 beg = 0;
  144.             }
  145.             break;
  146.  
  147.         case TPLUS:
  148.             if (beg != 0) {
  149.                 printf("Non-numeric second argument\n");
  150.                 return(-1);
  151.             }
  152.             i = valdot;
  153.             do {
  154.                 i++;
  155.                 if (i > msgCount) {
  156.                     printf("Referencing beyond EOF\n");
  157.                     return(-1);
  158.                 }
  159.             } while ((message[i - 1].m_flag & MDELETED) != f);
  160.             mark(i);
  161.             break;
  162.  
  163.         case TDASH:
  164.             if (beg == 0) {
  165.                 i = valdot;
  166.                 do {
  167.                     i--;
  168.                     if (i <= 0) {
  169.                         printf("Referencing before 1\n");
  170.                         return(-1);
  171.                     }
  172.                 } while ((message[i - 1].m_flag & MDELETED) != f);
  173.                 mark(i);
  174.             }
  175.             break;
  176.  
  177.         case TSTRING:
  178.             if (beg != 0) {
  179.                 printf("Non-numeric second argument\n");
  180.                 return(-1);
  181.             }
  182.             other++;
  183.             if (lexstring[0] == ':') {
  184.                 colresult = evalcol(lexstring[1]);
  185.                 if (colresult == 0) {
  186.                     printf("Unknown colon modifier \"%s\"\n",
  187.                         lexstring);
  188.                     return(-1);
  189.                 }
  190.                 colmod |= colresult;
  191.             }
  192.             else
  193.                 *np++ = savestr(lexstring);
  194.             break;
  195.  
  196.         case TDOLLAR:
  197.         case TUP:
  198.         case TDOT:
  199.             lexnumber = metamess(lexstring[0], f);
  200.             if (lexnumber == -1)
  201.                 return(-1);
  202.             goto number;
  203.  
  204.         case TSTAR:
  205.             if (other) {
  206.                 printf("Can't mix \"*\" with anything\n");
  207.                 return(-1);
  208.             }
  209.             star++;
  210.             break;
  211.  
  212.         case TERROR:
  213.             return -1;
  214.         }
  215.         tok = scan(&bufp);
  216.     }
  217.     lastcolmod = colmod;
  218.     *np = NOSTR;
  219.     mc = 0;
  220.     if (star) {
  221.         for (i = 0; i < msgCount; i++)
  222.             if ((message[i].m_flag & MDELETED) == f) {
  223.                 mark(i+1);
  224.                 mc++;
  225.             }
  226.         if (mc == 0) {
  227.             printf("No applicable messages.\n");
  228.             return(-1);
  229.         }
  230.         return(0);
  231.     }
  232.  
  233.     /*
  234.      * If no numbers were given, mark all of the messages,
  235.      * so that we can unmark any whose sender was not selected
  236.      * if any user names were given.
  237.      */
  238.  
  239.     if ((np > namelist || colmod != 0) && mc == 0)
  240.         for (i = 1; i <= msgCount; i++)
  241.             if ((message[i-1].m_flag & MDELETED) == f)
  242.                 mark(i);
  243.  
  244.     /*
  245.      * If any names were given, go through and eliminate any
  246.      * messages whose senders were not requested.
  247.      */
  248.  
  249.     if (np > namelist) {
  250.         for (i = 1; i <= msgCount; i++) {
  251.             for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
  252.                 if (**np == '/') {
  253.                     if (matchsubj(*np, i)) {
  254.                         mc++;
  255.                         break;
  256.                     }
  257.                 }
  258.                 else {
  259.                     if (matchsender(*np, i)) {
  260.                         mc++;
  261.                         break;
  262.                     }
  263.                 }
  264.             if (mc == 0)
  265.                 unmark(i);
  266.         }
  267.  
  268.         /*
  269.          * Make sure we got some decent messages.
  270.          */
  271.  
  272.         mc = 0;
  273.         for (i = 1; i <= msgCount; i++)
  274.             if (message[i-1].m_flag & MMARK) {
  275.                 mc++;
  276.                 break;
  277.             }
  278.         if (mc == 0) {
  279.             printf("No applicable messages from {%s",
  280.                 namelist[0]);
  281.             for (np = &namelist[1]; *np != NOSTR; np++)
  282.                 printf(", %s", *np);
  283.             printf("}\n");
  284.             return(-1);
  285.         }
  286.     }
  287.  
  288.     /*
  289.      * If any colon modifiers were given, go through and
  290.      * unmark any messages which do not satisfy the modifiers.
  291.      */
  292.  
  293.     if (colmod != 0) {
  294.         for (i = 1; i <= msgCount; i++) {
  295.             register struct coltab *colp;
  296.  
  297.             mp = &message[i - 1];
  298.             for (colp = &coltab[0]; colp->co_char; colp++)
  299.                 if (colp->co_bit & colmod)
  300.                     if ((mp->m_flag & colp->co_mask)
  301.                         != colp->co_equal)
  302.                         unmark(i);
  303.             
  304.         }
  305.         for (mp = &message[0]; mp < &message[msgCount]; mp++)
  306.             if (mp->m_flag & MMARK)
  307.                 break;
  308.         if (mp >= &message[msgCount]) {
  309.             register struct coltab *colp;
  310.  
  311.             printf("No messages satisfy");
  312.             for (colp = &coltab[0]; colp->co_char; colp++)
  313.                 if (colp->co_bit & colmod)
  314.                     printf(" :%c", colp->co_char);
  315.             printf("\n");
  316.             return(-1);
  317.         }
  318.     }
  319.     return(0);
  320. }
  321.  
  322. /*
  323.  * Turn the character after a colon modifier into a bit
  324.  * value.
  325.  */
  326. evalcol(col)
  327. {
  328.     register struct coltab *colp;
  329.  
  330.     if (col == 0)
  331.         return(lastcolmod);
  332.     for (colp = &coltab[0]; colp->co_char; colp++)
  333.         if (colp->co_char == col)
  334.             return(colp->co_bit);
  335.     return(0);
  336. }
  337.  
  338. /*
  339.  * Check the passed message number for legality and proper flags.
  340.  * If f is MDELETED, then either kind will do.  Otherwise, the message
  341.  * has to be undeleted.
  342.  */
  343. check(mesg, f)
  344. {
  345.     register struct message *mp;
  346.  
  347.     if (mesg < 1 || mesg > msgCount) {
  348.         printf("%d: Invalid message number\n", mesg);
  349.         return(-1);
  350.     }
  351.     mp = &message[mesg-1];
  352.     if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
  353.         printf("%d: Inappropriate message\n", mesg);
  354.         return(-1);
  355.     }
  356.     return(0);
  357. }
  358.  
  359. /*
  360.  * Scan out the list of string arguments, shell style
  361.  * for a RAWLIST.
  362.  */
  363.  
  364. getrawlist(line, argv, argc)
  365.     char line[];
  366.     char **argv;
  367.     int  argc;
  368. {
  369.     register char c, *cp, *cp2, quotec;
  370.     int argn;
  371.     char linebuf[BUFSIZ];
  372.  
  373.     argn = 0;
  374.     cp = line;
  375.     for (;;) {
  376.         for (; *cp == ' ' || *cp == '\t'; cp++)
  377.             ;
  378.         if (*cp == '\0')
  379.             break;
  380.         if (argn >= argc - 1) {
  381.             printf(
  382.             "Too many elements in the list; excess discarded.\n");
  383.             break;
  384.         }
  385.         cp2 = linebuf;
  386.         quotec = '\0';
  387.         while ((c = *cp) != '\0') {
  388.             cp++;
  389.             if (quotec != '\0') {
  390.                 if (c == quotec)
  391.                     quotec = '\0';
  392.                 else if (c == '\\')
  393.                     switch (c = *cp++) {
  394.                     case '\0':
  395.                         *cp2++ = *--cp;
  396.                         break;
  397.                     case '0': case '1': case '2': case '3':
  398.                     case '4': case '5': case '6': case '7':
  399.                         c -= '0';
  400.                         if (*cp >= '0' && *cp <= '7')
  401.                             c = c * 8 + *cp++ - '0';
  402.                         if (*cp >= '0' && *cp <= '7')
  403.                             c = c * 8 + *cp++ - '0';
  404.                         *cp2++ = c;
  405.                         break;
  406.                     case 'b':
  407.                         *cp2++ = '\b';
  408.                         break;
  409.                     case 'f':
  410.                         *cp2++ = '\f';
  411.                         break;
  412.                     case 'n':
  413.                         *cp2++ = '\n';
  414.                         break;
  415.                     case 'r':
  416.                         *cp2++ = '\r';
  417.                         break;
  418.                     case 't':
  419.                         *cp2++ = '\t';
  420.                         break;
  421.                     case 'v':
  422.                         *cp2++ = '\v';
  423.                         break;
  424.                     }
  425.                 else if (c == '^') {
  426.                     c = *cp++;
  427.                     if (c == '?')
  428.                         *cp2++ = '\177';
  429.                     /* null doesn't show up anyway */
  430.                     else if (c >= 'A' && c <= '_' ||
  431.                          c >= 'a' && c <= 'z')
  432.                         *cp2++ &= 037;
  433.                     else
  434.                         *cp2++ = *--cp;
  435.                 } else
  436.                     *cp2++ = c;
  437.             } else if (c == '"' || c == '\'')
  438.                 quotec = c;
  439.             else if (c == ' ' || c == '\t')
  440.                 break;
  441.             else
  442.                 *cp2++ = c;
  443.         }
  444.         *cp2 = '\0';
  445.         argv[argn++] = savestr(linebuf);
  446.     }
  447.     argv[argn] = NOSTR;
  448.     return argn;
  449. }
  450.  
  451. /*
  452.  * scan out a single lexical item and return its token number,
  453.  * updating the string pointer passed **p.  Also, store the value
  454.  * of the number or string scanned in lexnumber or lexstring as
  455.  * appropriate.  In any event, store the scanned `thing' in lexstring.
  456.  */
  457.  
  458. struct lex {
  459.     char    l_char;
  460.     char    l_token;
  461. } singles[] = {
  462.     '$',    TDOLLAR,
  463.     '.',    TDOT,
  464.     '^',    TUP,
  465.     '*',    TSTAR,
  466.     '-',    TDASH,
  467.     '+',    TPLUS,
  468.     '(',    TOPEN,
  469.     ')',    TCLOSE,
  470.     0,    0
  471. };
  472.  
  473. scan(sp)
  474.     char **sp;
  475. {
  476.     register char *cp, *cp2;
  477.     register int c;
  478.     register struct lex *lp;
  479.     int quotec;
  480.  
  481.     if (regretp >= 0) {
  482.         strcpy(lexstring, string_stack[regretp]);
  483.         lexnumber = numberstack[regretp];
  484.         return(regretstack[regretp--]);
  485.     }
  486.     cp = *sp;
  487.     cp2 = lexstring;
  488.     c = *cp++;
  489.  
  490.     /*
  491.      * strip away leading white space.
  492.      */
  493.  
  494.     while (c == ' ' || c == '\t')
  495.         c = *cp++;
  496.  
  497.     /*
  498.      * If no characters remain, we are at end of line,
  499.      * so report that.
  500.      */
  501.  
  502.     if (c == '\0') {
  503.         *sp = --cp;
  504.         return(TEOL);
  505.     }
  506.  
  507.     /*
  508.      * If the leading character is a digit, scan
  509.      * the number and convert it on the fly.
  510.      * Return TNUMBER when done.
  511.      */
  512.  
  513.     if (isdigit(c)) {
  514.         lexnumber = 0;
  515.         while (isdigit(c)) {
  516.             lexnumber = lexnumber*10 + c - '0';
  517.             *cp2++ = c;
  518.             c = *cp++;
  519.         }
  520.         *cp2 = '\0';
  521.         *sp = --cp;
  522.         return(TNUMBER);
  523.     }
  524.  
  525.     /*
  526.      * Check for single character tokens; return such
  527.      * if found.
  528.      */
  529.  
  530.     for (lp = &singles[0]; lp->l_char != 0; lp++)
  531.         if (c == lp->l_char) {
  532.             lexstring[0] = c;
  533.             lexstring[1] = '\0';
  534.             *sp = cp;
  535.             return(lp->l_token);
  536.         }
  537.  
  538.     /*
  539.      * We've got a string!  Copy all the characters
  540.      * of the string into lexstring, until we see
  541.      * a null, space, or tab.
  542.      * If the lead character is a " or ', save it
  543.      * and scan until you get another.
  544.      */
  545.  
  546.     quotec = 0;
  547.     if (c == '\'' || c == '"') {
  548.         quotec = c;
  549.         c = *cp++;
  550.     }
  551.     while (c != '\0') {
  552.         if (c == quotec) {
  553.             cp++;
  554.             break;
  555.         }
  556.         if (quotec == 0 && (c == ' ' || c == '\t'))
  557.             break;
  558.         if (cp2 - lexstring < STRINGLEN-1)
  559.             *cp2++ = c;
  560.         c = *cp++;
  561.     }
  562.     if (quotec && c == 0) {
  563.         fprintf(stderr, "Missing %c\n", quotec);
  564.         return TERROR;
  565.     }
  566.     *sp = --cp;
  567.     *cp2 = '\0';
  568.     return(TSTRING);
  569. }
  570.  
  571. /*
  572.  * Unscan the named token by pushing it onto the regret stack.
  573.  */
  574.  
  575. regret(token)
  576. {
  577.     if (++regretp >= REGDEP)
  578.         panic("Too many regrets");
  579.     regretstack[regretp] = token;
  580.     lexstring[STRINGLEN-1] = '\0';
  581.     string_stack[regretp] = savestr(lexstring);
  582.     numberstack[regretp] = lexnumber;
  583. }
  584.  
  585. /*
  586.  * Reset all the scanner global variables.
  587.  */
  588.  
  589. scaninit()
  590. {
  591.     regretp = -1;
  592. }
  593.  
  594. /*
  595.  * Find the first message whose flags & m == f  and return
  596.  * its message number.
  597.  */
  598.  
  599. first(f, m)
  600. {
  601.     register struct message *mp;
  602.  
  603.     if (msgCount == 0)
  604.         return 0;
  605.     f &= MDELETED;
  606.     m &= MDELETED;
  607.     for (mp = dot; mp < &message[msgCount]; mp++)
  608.         if ((mp->m_flag & m) == f)
  609.             return mp - message + 1;
  610.     for (mp = dot-1; mp >= &message[0]; mp--)
  611.         if ((mp->m_flag & m) == f)
  612.             return mp - message + 1;
  613.     return 0;
  614. }
  615.  
  616. /*
  617.  * See if the passed name sent the passed message number.  Return true
  618.  * if so.
  619.  */
  620.  
  621. matchsender(str, mesg)
  622.     char *str;
  623. {
  624.     register char *cp, *cp2, *backup;
  625.  
  626.     if (!*str)    /* null string matches nothing instead of everything */
  627.         return 0;
  628.     backup = cp2 = nameof(&message[mesg - 1], 0);
  629.     cp = str;
  630.     while (*cp2) {
  631.         if (*cp == 0)
  632.             return(1);
  633.         if (raise(*cp++) != raise(*cp2++)) {
  634.             cp2 = ++backup;
  635.             cp = str;
  636.         }
  637.     }
  638.     return(*cp == 0);
  639. }
  640.  
  641. /*
  642.  * See if the given string matches inside the subject field of the
  643.  * given message.  For the purpose of the scan, we ignore case differences.
  644.  * If it does, return true.  The string search argument is assumed to
  645.  * have the form "/search-string."  If it is of the form "/," we use the
  646.  * previous search string.
  647.  */
  648.  
  649. char lastscan[128];
  650.  
  651. matchsubj(str, mesg)
  652.     char *str;
  653. {
  654.     register struct message *mp;
  655.     register char *cp, *cp2, *backup;
  656.  
  657.     str++;
  658.     if (strlen(str) == 0)
  659.         str = lastscan;
  660.     else
  661.         strcpy(lastscan, str);
  662.     mp = &message[mesg-1];
  663.     
  664.     /*
  665.      * Now look, ignoring case, for the word in the string.
  666.      */
  667.  
  668.     cp = str;
  669.     cp2 = hfield("subject", mp);
  670.     if (cp2 == NOSTR)
  671.         return(0);
  672.     backup = cp2;
  673.     while (*cp2) {
  674.         if (*cp == 0)
  675.             return(1);
  676.         if (raise(*cp++) != raise(*cp2++)) {
  677.             cp2 = ++backup;
  678.             cp = str;
  679.         }
  680.     }
  681.     return(*cp == 0);
  682. }
  683.  
  684. /*
  685.  * Mark the named message by setting its mark bit.
  686.  */
  687.  
  688. mark(mesg)
  689. {
  690.     register int i;
  691.  
  692.     i = mesg;
  693.     if (i < 1 || i > msgCount)
  694.         panic("Bad message number to mark");
  695.     message[i-1].m_flag |= MMARK;
  696. }
  697.  
  698. /*
  699.  * Unmark the named message.
  700.  */
  701.  
  702. unmark(mesg)
  703. {
  704.     register int i;
  705.  
  706.     i = mesg;
  707.     if (i < 1 || i > msgCount)
  708.         panic("Bad message number to unmark");
  709.     message[i-1].m_flag &= ~MMARK;
  710. }
  711.  
  712. /*
  713.  * Return the message number corresponding to the passed meta character.
  714.  */
  715.  
  716. metamess(meta, f)
  717. {
  718.     register int c, m;
  719.     register struct message *mp;
  720.  
  721.     c = meta;
  722.     switch (c) {
  723.     case '^':
  724.         /*
  725.          * First 'good' message left.
  726.          */
  727.         for (mp = &message[0]; mp < &message[msgCount]; mp++)
  728.             if ((mp->m_flag & MDELETED) == f)
  729.                 return(mp - &message[0] + 1);
  730.         printf("No applicable messages\n");
  731.         return(-1);
  732.  
  733.     case '$':
  734.         /*
  735.          * Last 'good message left.
  736.          */
  737.         for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
  738.             if ((mp->m_flag & MDELETED) == f)
  739.                 return(mp - &message[0] + 1);
  740.         printf("No applicable messages\n");
  741.         return(-1);
  742.  
  743.     case '.':
  744.         /* 
  745.          * Current message.
  746.          */
  747.         m = dot - &message[0] + 1;
  748.         if ((dot->m_flag & MDELETED) != f) {
  749.             printf("%d: Inappropriate message\n", m);
  750.             return(-1);
  751.         }
  752.         return(m);
  753.  
  754.     default:
  755.         printf("Unknown metachar (%c)\n", c);
  756.         return(-1);
  757.     }
  758. }
  759.